import { Dep } from './observer/dep';
import {observe, defineReactive, toggleObserving} from './observer/index'
import { Watcher } from './observer/watcher';
import {isUpdatingComponent} from './vdom/index'
function proxy(object, targetKey, key){
    Object.defineProperty(object, key, {
        get(){
            return object[targetKey][key]
        },
        set(val){
            object[targetKey][key] = val;
        }
    })
}
function initComputed(vm){
    const $options = vm.$options;
    const computed = $options.computed;
    for (let computedFn in computed){
        const userDef = computed[computedFn];

        const watcher = new Watcher(vm, userDef, ()=>{}, {lazy: true});
        Object.defineProperty(vm, computedFn, {
            get(){
                if (watcher.dirty){
                    /*
                    computed:{
                        total(){
                            return this.count * this.money
                        }
                    }
                    让 total watcher 记住 this.count 与 this.money
                    */
                    watcher.evaluate(); 
                }
                // total watcher已经出栈了, 现在还得让 this.count 与 this.money 记住渲染watcher
                // 因为 total watcher只是让 dirty为true, 并不会真正渲染页面。
                if (Dep.target){
                    watcher.depend()
                }
                return watcher.value;
            }
        })
    }
}
function initMethods(vm){
    const $options = vm.$options;
    const methods = $options.methods;
    for (let methodName in methods){
        const methodDef = methods[methodName];
        vm[methodName] = function(...args){
            return methodDef.call(vm, ...args);
        };
    }
}
function initData(vm){
    let data = vm.$options.data;
    // data是函数的话就去执行下
    if (typeof data === 'function'){
        data = data.call(vm);
    }
    // 放在 _data 上面以便访问
    vm._data = data;
    // 将对象变成getter, setter
    observe(data);

    // 将_data 代理到 vm上面
    const keys = Object.keys(data);
    for (let i=0; i<keys.length; i++){
        proxy(vm, '_data', keys[i]);
    }
}   

function initProps(vm){
    if (!vm.$options.props)return;
    //
    vm._props = observe({}, true);
    const $options = vm.$options;
    /*
    props: {a: {type: null}}
    propData: {a: 0}
    */
    const {props, propData} = $options;
    // 
    toggleObserving(false);
    for (let key in props){
        // vue在此处会对props类型进行验证
        let value = propData[key];
        defineReactive(vm._props, key, value, true, function(){
            if (!isUpdatingComponent){
                console.warn(`props 不能在子组件里被覆盖哦`);
            }
        });
        proxy(vm, '_props', key);
    }
    toggleObserving(true);    
}
function initWatch(vm){
    const $options = vm.$options;
    if (!$options.watch)return;
    for (let watchKey in $options.watch){
        const watchDef = $options.watch[watchKey];
        // new Watcher(vm, 'a.b.c', ()=>{});
        vm.$watch(vm, watchKey, watchDef);
    }
}
export function initState(vm){
    let options = vm.$options;
    // 顺序 props methods data computed watch
    if (options.props){
        initProps(vm);
    }
    if (options.methods){
        initMethods(vm);
    }
    if (options.data){
        // 初始化data
        initData(vm);
    }
    if (options.computed){
        initComputed(vm);
    }
    if (options.watch){
        initWatch(vm);
    }
}